articles

Home / DeveloperSection / Articles / Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC using Entity Framework

Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC using Entity Framework

Anupam Mishra9060 14-Mar-2016
Introduction:

The repository and unit of work patterns are intended to create an abstraction layer between the data access layer and the business logic layer of an application. Implementing these patterns can help insulate our application from changes in the data store and can facilitate automated unit testing or test-driven development (TDD).

In this article, I am implementing a repository class for Student entity type. I have created a one interface class i.e. “IStudentRepository.cs” and one repository class i.e. “”StudentRepository.cs. When we instantiate the repository in controller, we will use the interface so that the controller will accept a reference to any object that implements the repository interface. When the controller runs under a web server, it receives a repository that works with the Entity Framework. The unit of work class coordinates the work of multiple repositories by creating a single database context class shared by all of them.  

In simple MVC application, we are not implementing this pattern because we do not know about how to create an abstraction layer between data access layer and the business logic layer of an application. Before implementing this type of repository, we have directly connected IIS server to the controller and the controller is a directly connected to a context file i.e. Database. We have not any layer between them. The Repository is providing a much facility for testing, security and divided multiple works with same context file for multiple controller, when we created a unit of work. When we use a Repository, Repository is connected with a controller and Unit of Work along with the database. And, its play a centralized role for each one, so we are easily design an application. We have also designed a generic type entity in our application that reduce the line of code and accessible for all entity types.

Note: Here, we have a one model of Student. I have also created view for curd operation separately (i.e. Create,Update,Delete,Details and Index view).  The Student model structure is as follows:

 

publicclassStudent
        {
            [Key]
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            publicint StudentID { get; set; }
            [Required]
            [StringLength(50,MinimumLength=1)]
            [Display(Name = "First Name")]
            publicstring FirstName { get; set; }
            [Required]
            [StringLength(50, MinimumLength = 1)]
            [Display(Name = "Last Name")]
            publicstring LastName { get; set; }
            [DataType(DataType.DateTime)]
            [Display(Name = "Enrollment Date")]
            publicDateTime EnrollmentDate { get; set; }
            [Display(Name = "Full Name")]
            publicstring FullName
            {
                get
                {
                    return FirstName + " " + LastName;
                }
            }
            publicvirtualICollection<Enrollment> Enrollment { get; set; }
 
 
publicclassMyUniversityDBContext : DbContext
    {
         publicDbSet<Student> Student { get; set; }
    }
   }

 

 And here, I have created a controller of Student model, named i.e. “StudentController”.


Code is as follows:

 

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MyUniversity.Models;
using PagedList;
 
namespace MyUniversity.Controllers
{
    publicclassStudentController : Controller
    {
        private MyUniversityDBContext db = new MyUniversityDBContext();
 
        //
        // GET: /Student/
 
        public ViewResult Index(string sortOrder, string currentFilter, string  searchString, int? page)
        {
            ViewBag.CurrentSort = sortOrder;
            ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
            ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date";
 
            if (searchString != null)
            {
                page = 1;
            }
            else
            {
                searchString = currentFilter;
            }
            ViewBag.CurrentFilter = searchString;
            var students = from s in db.Student
                           select s;
            if (!String.IsNullOrEmpty(searchString))
            {
                students = students.Where(s => s.FirstName.ToUpper().Contains (searchString.ToUpper())                                   ||s.LastName.ToUpper().Contains(searchString.ToUpper()));
            }
            switch (sortOrder)
            {
                case"name_desc":
                    students = students.OrderByDescending(s => s.LastName);
                    break;
                case"Date":
                    students = students.OrderBy(s => s.EnrollmentDate);
                    break;
                case"date_desc":
                    students = students.OrderByDescending(s => s.EnrollmentDate);
                    break;
                default// Name ascending
                    students = students.OrderBy(s => s.LastName);
                    break;
            }
 
            int pageSize = 3;
            int pageNumber = (page ?? 1);
            return View(students.ToPagedList (pageNumber, pageSize));
        }
        // GET:/Student/Details/5
 
        public ActionResult Details(int id = 0)
        {
            Student student = db.Student.Find(id);
            if (student == null)
            {
                return HttpNotFound();
            }
            return View(student);
        }
        // GET: /Student/Create
        public ActionResult Create()
        {
            return View();
        }
        // POST: /Student/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Student student)
        {
            if (ModelState.IsValid)
            {
                db.Student.Add(student);
                db.SaveChanges();
                return RedirectToAction("Index");
            }
 
            return View(student);
        }
 
       // GET: /Student/Edit/5
        public ActionResult Edit(int id = 0)
        {
            Student student = db.Student.Find(id);
            if (student == null)
            {
                return HttpNotFound();
            }
            return View(student);
        }
        // POST: /Student/Edit/5 
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(Student student)
        {
            if (ModelState.IsValid)
            {
                db.Entry(student).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(student);
        }
 
        //
        // GET: /Student/Delete/5
        public ActionResult Delete(int id = 0)
        {
            Student student = db.Student.Find(id);
            if (student == null)
            {
                return HttpNotFound();
            }
            return View(student);
        }
        // POST: /Student/Delete/5 
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            Student student = db.Student.Find(id);
            db.Student.Remove(student);
            db.SaveChanges();
            return RedirectToAction("Index");
        } 
        protectedoverridevoid Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}

 

 

Index view:

 

@model PagedList.IPagedList<MyUniversity.Models.Student>
@using PagedList.Mvc;
<linkhref="~/Content/PagedList.css"rel="stylesheet"/>
    <scriptsrc="~/Scripts/jquery-1.8.2.min.js"></script>
<linkhref="~/Content/anupam.css"rel="stylesheet"/>
 
@using MyUniversity.Models
  @{
    ViewBag.Title = "Index";
    var CourseList = Session["CourseList"] asList<Course>;}
 
    <h2>Index</h2>
<p>
    @Html.ActionLink("Create New""Create")
    @using (Html.BeginForm("Index", "Student", FormMethod.Get))
{
    <p>
        Find by name: @Html.TextBox("SearchString", ViewBag.CurrentFilter asstring
        <inputtype="submit"value="Search"/>
    </p>
}<table>
   <tr>
        <th>
            @Html.ActionLink("First Name", "Index", new { sortOrder = ViewBag.NameSortParm })
        </th>
        <th>Last Name
        </th>
        <th>
            @Html.ActionLink("Enrollment Date", "Index", new { sortOrder = ViewBag.DateSortParm })
        </th>
       <th>Full Name
        </th>
        <th></th>
  
    </tr>
 
@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.FirstName)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.LastName)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.EnrollmentDate)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.FullName)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.StudentID }) |
            @Html.ActionLink("Details", "Details", new { id=item.StudentID }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.StudentID })
        </td>
    </tr>
} 
</table>
<br/>
Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount
@Html.PagedListPager( Model, page => Url.Action("Index", new { page, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter }) )
 
 

 

Create View:

 

@model MyUniversity.Models.Student
 
@{
    ViewBag.Title = "Create";
}
 
<h2>Create</h2>
 
@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)
 
    <fieldset>
        <legend>Student</legend>
 
        <divclass="editor-label">
            @Html.LabelFor(model => model.FirstName)
        </div>
        <divclass="editor-field">
            @Html.EditorFor(model => model.FirstName)
            @Html.ValidationMessageFor(model => model.FirstName)
        </div>
 
        <divclass="editor-label">
            @Html.LabelFor(model => model.LastName)
        </div>
        <divclass="editor-field">
            @Html.EditorFor(model => model.LastName)
            @Html.ValidationMessageFor(model => model.LastName)
        </div>
 
        <divclass="editor-label">
            @Html.LabelFor(model => model.EnrollmentDate)
        </div>
        <divclass="editor-field">
            @Html.EditorFor(model => model.EnrollmentDate)
            @Html.ValidationMessageFor(model =>model.EnrollmentDate)
        </div>
 
        <p>
            <inputtype="submit"value="Create"/>
        </p>
    </fieldset>
}
 
<div>
    @Html.ActionLink("Back to List", "Index")
</div>
 
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}
 


Details view:

 

@model MyUniversity.Models.Student
 
@{
    ViewBag.Title = "Details";
}
 
<h2>Details</h2>
 
<fieldset>
    <legend>Student</legend>
 
    <divclass="display-label">
         @Html.DisplayNameFor(model => model.FirstName)
    </div>
    <divclass="display-field">
        @Html.DisplayFor(model => model.FirstName)
    </div>
 
    <divclass="display-label">
         @Html.DisplayNameFor(model => model.LastName)
    </div>
    <divclass="display-field">
        @Html.DisplayFor(model => model.LastName)
    </div>
 
    <divclass="display-label">
         @Html.DisplayNameFor(model => model.EnrollmentDate)
    </div>
    <divclass="display-field">
        @Html.DisplayFor(model => model.EnrollmentDate)
    </div>
 
    <divclass="display-label">
         @Html.DisplayNameFor(model => model.FullName)
    </div>
    <divclass="display-field">
        @Html.DisplayFor(model => model.FullName)
    </div>
</fieldset>
<p>
    @Html.ActionLink("Edit", "Edit", new { id=Model.StudentID }) |
    @Html.ActionLink("Back to List", "Index")
</p>

 

Edit View:

 

@model MyUniversity.Models.Student
 
@{
    ViewBag.Title = "Edit";
} 
<h2>Edit</h2> 
@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)
 
    <fieldset>
        <legend>Student</legend>
 
        @Html.HiddenFor(model => model.StudentID)
 
        <divclass="editor-label">
            @Html.LabelFor(model => model.FirstName)
        </div>
        <divclass="editor-field">
            @Html.EditorFor(model => model.FirstName)
            @Html.ValidationMessageFor(model => model.FirstName)
        </div>
 
        <divclass="editor-label">
            @Html.LabelFor(model => model.LastName)
        </div>
        <divclass="editor-field">
            @Html.EditorFor(model => model.LastName)
            @Html.ValidationMessageFor(model => model.LastName)
        </div>
 
        <divclass="editor-label">
            @Html.LabelFor(model => model.EnrollmentDate)
        </div>
        <divclass="editor-field">
            @Html.EditorFor(model => model.EnrollmentDate)
            @Html.ValidationMessageFor(model => model.EnrollmentDate)
        </div>
 
        <p>
            <inputtype="submit"value="Save"/>
        </p>
    </fieldset>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div> 
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}
 


Delete View:

 

@model MyUniversity.Models.Student
 
@{
    ViewBag.Title = "Delete";
}
 <h2>Delete</h2> 
<h3>Are you sure you want to delete this?</h3>
<fieldset>
    <legend>Student</legend> 
    <divclass="display-label">
         @Html.DisplayNameFor(model => model.FirstName)
    </div>
    <divclass="display-field">
        @Html.DisplayFor(model => model.FirstName)
    </div> 
    <divclass="display-label">
         @Html.DisplayNameFor(model => model.LastName)
    </div>
    <divclass="display-field">
        @Html.DisplayFor(model => model.LastName)
    </div> 
    <divclass="display-label">
         @Html.DisplayNameFor(model => model.EnrollmentDate)
    </div>
    <divclass="display-field">
        @Html.DisplayFor(model => model.EnrollmentDate)
    </div> 
    <divclass="display-label">
         @Html.DisplayNameFor(model => model.FullName)
    </div>
    <divclass="display-field">
        @Html.DisplayFor(model => model.FullName)
    </div>
</fieldset>
@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
    <p>
        <inputtype="submit"value="Delete"/> |
        @Html.ActionLink("Back to List", "Index")
    </p>
}

  

For creating a Repository, we have follow the following step:


1.      Right click on project under Solution Explorer and create a new folder named


i.e. “DAL 

2.      Under  DAL folder create a new  class file for interface


3.      Named i.e.  IStudentRepository

 

 And write the following code:

  publicinterfaceIStudentRepository : IDisposable
    {
        IEnumerable<Student> GetStudents();
        Student GetStudentByID(int studentId);
        void InsertStudent(Student student);
        void DeleteStudent(int studentID);
        void UpdateStudent(Student student);
        void Save();
    }

 

Now, we have created another class named “StudentRepository.cs” for implement above interface for all activities in student controller. Code is as follows:

publicclassStudentRepository : IStudentRepository, IDisposable
    {
        privateMyUniversityDBContext context;
        public StudentRepository(MyUniversityDBContext context)
        {
            this.context = context;
        } 
        publicIEnumerable<Student> GetStudents()
        {
            return context.Student.ToList();
        } 
        publicStudent GetStudentByID(int id)
        {
            return context.Student.Find(id);
        } 
        publicvoid InsertStudent(Student student)
        {
            context.Student.Add(student);
        } 
        publicvoid DeleteStudent(int studentID)
        {
            Student student = context.Student.Find(studentID);
            context.Student.Remove(student);
        }
        publicvoid UpdateStudent(Student student)
        {
            context.Entry(student).State = EntityState.Modified;
        }
        publicvoid Save()
        {
            context.SaveChanges();
        } 
        privatebool disposed = false; 
        protectedvirtualvoid Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    context.Dispose();
                }
            }
            this.disposed = true;
        } 
        publicvoid Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }


Output:

Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC using Entity Framework


Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC using Entity Framework


Now, I want to create a unit of work for the next level access. For this, I have choose a another model “Course” that are part of above”MyUniversity” project. The Course model is below:

publicclassCourse
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        publicint CourseID { get; set; }
        [StringLength(50, MinimumLength = 3)]
        publicstring Title { get; set; }
        [Range(0, 5)]
        publicint Credits { get; set; }
        [Display(Name="Department")]
        publicint DepartmentID { get; set; }
 
        publicvirtualDepartment Department { get; set; }
    }

 

(Note that here, Course model is same as above Student model curd operations. It means, using Course model, I have already created a view for Create,Update,Delete,Details and Index.  )

For creating a Unit of Work of Course model, we have need a generic repository because we have not want to create a separate repository for each model. So, removing this overhead we have created a GenericRepository class .(In above Course model we have need two type of repository one for Course and another for Department. So I have choose creating generic repository for both to access a single context file). Code is as follow for GenericRepository.cs:


using MyUniversity.Models;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Web;
 
namespace MyUniversity.DAL
{
    publicclassGenericRepository<TEntity> where TEntity : class
    {
        internalMyUniversityDBContext context;
        internalDbSet<TEntity> dbSet;
 
        public GenericRepository(MyUniversityDBContext context)
        {
            this.context = context;
            this.dbSet = context.Set<TEntity>();
        }
 
        publicvirtualIEnumerable<TEntity> Get(
            Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            string includeProperties = "")
        {
            IQueryable<TEntity> query = dbSet;
 
            if (filter != null)
            {
                query = query.Where(filter);
            }
 
            foreach (var includeProperty in includeProperties.Split
                (newchar[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }
 
            if (orderBy != null)
            {
                return orderBy(query).ToList();
            }
            else
            {
                return query.ToList();
            }
        }
 
        publicvirtual TEntity GetByID(object id)
        {
            return dbSet.Find(id);
        }
 
        publicvirtualvoid Insert(TEntity entity)
        {
            dbSet.Add(entity);
        }
 
        publicvirtualvoid Delete(object id)
        {
            TEntity entityToDelete = dbSet.Find(id);
            Delete(entityToDelete);
        }
 
        publicvirtualvoid Delete(TEntity entityToDelete)
        {
            if (context.Entry(entityToDelete).State == EntityState.Detached)
            {
                dbSet.Attach(entityToDelete);
            }
            dbSet.Remove(entityToDelete);
        }
 
        publicvirtualvoid Update(TEntity entityToUpdate)
        {
            dbSet.Attach(entityToUpdate);
            context.Entry(entityToUpdate).State = EntityState.Modified;
        }
    }
}

 

Now, I have creating a unit of work of the Course model. Code is as follow: 

 
using MyUniversity.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
 
namespace MyUniversity.DAL
{
    publicclassUnitOfWork : IDisposable
    {
        privateMyUniversityDBContext context = newMyUniversityDBContext();
        privateGenericRepository<Department> departmentRepository;
        privateGenericRepository<Course> courseRepository; 
        publicGenericRepository<Department> DepartmentRepository
        {
            get
            {
 
                if (this.departmentRepository == null)
                {
                    this.departmentRepository = newGenericRepository<Department>(context);
                }
                return departmentRepository;
            }
        }
        publicGenericRepository<Course> CourseRepository
        {
            get
            {
 
                if (this.courseRepository == null)
                {
                    this.courseRepository = newGenericRepository<Course>(context);
                }
                return courseRepository;
            }
        } 
        publicvoid Save()
        {
            context.SaveChanges();
        } 
        privatebool disposed = false; 
        protectedvirtualvoid Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    context.Dispose();
                }
            }
            this.disposed = true;
        } 
        publicvoid Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}


And now, change the CourseConroller is as follow:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MyUniversity.Models;
using MykUniversity.DAL;
namespace MyUniversity.Controllers
{
    publicclassCourseController : Controller
    {
        privateMyUniversityDBContext db = newMyUniversityDBContext();
 
        privateUnitOfWork unitOfWork = newUnitOfWork();
        //
        // GET: /Course/ 
        publicViewResult Index()
        {
            var course = unitOfWork.CourseRepository.Get(includeProperties: "Department");
            return View(course.ToList());
        } 
        //
        // GET: /Course/Details/5 
        publicActionResult Details(int id = 0)
        {
            Course course = unitOfWork.CourseRepository.GetByID(id);
            if (course == null)
            {
                return HttpNotFound();
            }
            return View(course);
        } 

        // GET: /Course/Create
        publicActionResult Create()
        {
            ViewBag.DepartmentID = newSelectList(db.Department, "DepartmentID", "Name");
            return View();
        }
        // POST: /Course/Create 
        [HttpPost]
        [ValidateAntiForgeryToken]
        publicActionResult Create(Course course)
        {
            if (ModelState.IsValid)
            {               unitOfWork.CourseRepository.Insert(course);
                unitOfWork.Save();
                return RedirectToAction("Index");
            }
 
            ViewBag.DepartmentID = newSelectList(db.Department, "DepartmentID", "Name", course.DepartmentID);
            return View(course);
        }
        // GET: /Course/Edit/5 
        publicActionResult Edit(int id = 0)
        {
            Course course = unitOfWork.CourseRepository.GetByID(id);
            if (course == null)
            {
                return HttpNotFound();
            }
            ViewBag.DepartmentID = newSelectList(db.Department, "DepartmentID", "Name", course.DepartmentID);
            return View(course);
        }
        // POST: /Course/Edit/5 
        [HttpPost]
        [ValidateAntiForgeryToken]
        publicActionResult Edit(Course course)
        {
            if (ModelState.IsValid)
            {                unitOfWork.CourseRepository.Update(course);
                unitOfWork.Save();
                return RedirectToAction("Index");
            }
            ViewBag.DepartmentID = newSelectList(db.Department, "DepartmentID", "Name", course.DepartmentID);
            return View(course);
        }
        // GET: /Course/Delete/5
 
        publicActionResult Delete(int id = 0)
        {
            Course course = db.Course.Find(id);
            if (course == null)
            {
                return HttpNotFound();
            }
            return View(course);
        }

        // POST: /Course/Delete/5 
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        publicActionResult DeleteConfirmed(int id)
        {
            Course course = db.Course.Find(id);
            db.Course.Remove(course);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
       
        protectedoverridevoid Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}




Leave Comment

Comments

Liked By